#!/bin/sh -ef
#
# Copyright (C) 2003-2019  Dmitry V. Levin <ldv@altlinux.org>
# Copyright (C) 2007  Alexey Tourbin <at@altlinux.org>
# 
# The hsh-initroot utility for the hasher project
#
# SPDX-License-Identifier: GPL-2.0-or-later
#

. hsh-sh-functions

show_help()
{
	cat <<EOF
hsh-initroot - fill hasher chroot created by hsh-mkchroot with initial set of packages.

Usage: $PROG [options] [<path-to-workdir>]

<path-to-workdir> must be valid hasher directory.

Options:
  --excludedocs             do not install documentation files;
  --hasher-priv-dir=DIR     alternative hasher-priv directory;
  --install-langs=LI        colon-separated list of languages to install;
  --cache-compress=FILE     alternative cache compress executable;
  --cache-dir=DIR           alternative cache directory;
  --no-cache                do not use cache;
  --no-contents-indices     do not use content indices;
  --no-repackage-source     do not repackage source along with binaries;
  --nprocs=NUMBER           number of CPUs to use;
  --number=NUMBER           subconfig identifier;
  --packager=EMAIL          override default packager tag;
  --pkg-build-list=LIST     override default build package file list;
  --pkg-init-list=LIST      override default initial package file list;
  --repackage-source        repackage source along with binaries;
  --save-fakeroot           save fakeroot state;
  --wait-lock               wait for workdir and hasher-priv locks;
  --no-wait-lock            do not wait for workdir and hasher-priv locks;
  -q, --quiet               try to be more quiet;
  -v, --verbose             print a message for each action;
  -V, --version             print program version and exit;
  -h, --help                show this text and exit.
    
Report bugs to http://bugs.altlinux.ru/

EOF
	exit
}

TEMP=`getopt -n $PROG -o $getopt_common_opts -l cache-compress:,cache-dir:,contents-index-all:,contents-index-bin:,excludedocs,hasher-priv-dir:,install-langs:,no-cache,no-contents-indices,no-lock,no-repackage-source,nprocs:,number:,packager:,pkg-build-list:,pkg-init-list:,repackage-source,save-fakeroot,wait-lock,no-wait-lock,$getopt_common_longopts -- "$@"` ||
	show_usage
eval set -- "$TEMP"

: ${init_list:=$pkg_init_list}
: ${build_list:=$pkg_build_list}

while :; do
	case "$1" in
		--excludedocs) exclude_docs=--excludedocs
			;;
		--hasher-priv-dir)
			hasher_priv_dir="$(opt_check_dir "$1" "$2")"
			shift
			;;
		--cache-compress) shift; cache_compress="$1"
			;;
		--cache-dir) shift; cache_dir="$1"
			;;
		--install-langs) shift; install_langs="$1"
			;;
		--no-cache) no_cache=1
			;;
		--no-contents-indices) no_contents_indices=1
			;;
		--no-lock) no_lock=1
			;;
		--no-repackage-source) repackage_source=
			;;
		--nprocs)
			nprocs="$(opt_check_number "$1" "$2")"
			shift
			;;
		--number)
			number="$(opt_check_number_ge_0 "$1" "$2")"
			shift
			;;
		--packager) shift; packager="$1"
			;;
		--pkg-build-list) shift
			[ "$1" != "${1#+}" -a -n "${1#+}" ] && 
				build_list="$build_list,${1#+}" ||
				build_list="$1"
			;;
		--pkg-init-list) shift
			[ "$1" != "${1#+}" -a -n "${1#+}" ] && 
				init_list="$init_list,${1#+}" ||
				init_list="$1"
			;;
		--repackage-source) repackage_source=1
			;;
		--save-fakeroot) save_fakeroot=1
			;;
		--wait-lock) lock_nowait=
			;;
		--no-wait-lock) lock_nowait=1
			;;
		--contents-index-*)
			message "Warning: obsolete option $1 ignored."
			shift
			;;
		--) shift; break
			;;
		*) parse_common_option "$1"
			;;
	esac
	shift
done

if [ -z "$workdir" ]; then
	# Exactly one argument.
	[ "$#" -ge 1 ] || show_usage 'Insufficient arguments.'
	[ "$#" -le 1 ] || show_usage 'Too many arguments.'
else
	# At most one argument.
	[ "$#" -le 1 ] || show_usage 'Too many arguments.'
fi

if [ "$#" -ge 1 ]; then
	set_workdir "$1"
	shift
else
	set_workdir
fi

lock_workdir
[ -d "$chroot" ] || fatal "$chroot: cannot access chroot."
[ -d "$aptbox" ] || fatal "$aptbox: cannot access aptbox."
deduce_lock_hasher_priv

[ -z "$save_fakeroot" ] || init_list="$init_list cpio"

mkdir -p -m700 $verbose -- "$cache_dir" >&2
cache_dir="$(readlink -ev -- "$cache_dir")"

rm -rf $verbose "$cache_dir"/chroot/{list,package}.new >&2
mkdir -p -m700 $verbose "$cache_dir"/chroot/{list,package}{,.new} >&2
mkdir -p -m700 $verbose "$cache_dir"/contents >&2

uid0="$(id -u)"
gid0="$(id -g)"

ugid1="$("$getugid1" ${number:+-$number})" ||
	fatal 'getugid1 failed.'
uid1="$(printf %s "$ugid1" |cut -d: -f1)"
gid1="$(printf %s "$ugid1" |cut -d: -f2)"

ugid2="$("$getugid2" ${number:+-$number})" ||
	fatal 'getugid2 failed.'
uid2="$(printf %s "$ugid2" |cut -d: -f1)"
gid2="$(printf %s "$ugid2" |cut -d: -f2)"

[ -n "$nprocs" ] ||
	nprocs="$(N=`nproc ||:`; [ "$N" -gt 0 ] 2>/dev/null && printf %s "$N" || echo 1)"
opt_check_number nprocs "$nprocs" > /dev/null

check_tty

init_list=`printf %s "$init_list" |LC_ALL=C tr -s ',[:space:]' ' ' |sed -e 's/^ \+//;s/ \+$//'`
[ -n "$init_list" ] ||
	fatal 'Initial package file list is empty, cannot setup chroot.'

cookie_file="$cache_dir/chroot/cookie"
aptbox_archive="$cache_dir/chroot/aptbox.tar"

if [ -n "$unchecked_initroot_cache" ] &&
   [ -s "$cookie_file" -a -s "$aptbox_archive" ] &&
   [ "$unchecked_initroot_cache" = "$(cat -- "$cookie_file")" ]; then
	verbose 'initroot cache is unchecked, initial package file list setup bypassed.'
else
	initlist="$(print_uris $init_list)" ||
		fatal 'Failed to generate initial package file list.'
	[ -n "$initlist" ] &&
		verbose 'Generated initial package file list.' ||
		fatal 'Failed to generate non-empty initial package file list; check your sources.list'

	# reorder to get setup and filesystem first.
	initlist="
$(printf %s "$initlist" |
  LC_ALL=C grep '/setup[-_]' ||:)
$(printf %s "$initlist" |
  LC_ALL=C grep '/filesystem[-_]' ||:)
$(printf %s "$initlist" |
  LC_ALL=C grep -E -v '/(setup|filesystem)[-_]' ||:)"
	initlist="$(printf %s "$initlist" |
		    LC_ALL=C grep -v '^$' ||:)"
fi

build_list=`printf %s "$build_list" |LC_ALL=C tr -s ',[:space:]' ' ' |sed -e 's/^ \+//;s/ \+$//'`
if [ -n "$build_list" ]; then
	if [ -n "$unchecked_initroot_cache" ] &&
	   [ -s "$cookie_file" -a -s "$aptbox_archive" ] &&
	   [ "$unchecked_initroot_cache" = "$(cat -- "$cookie_file")" ]; then
		verbose 'initroot cache is unchecked, build package file list setup bypassed.'
		buildlist=' '
	else
		buildlist="$(print_uris $build_list)" ||
			fatal 'Failed to generate build package file list.'
		buildlist="$(
			printf '%s\n' "$buildlist" |
			while read bp; do
				[ -n "$bp" ] || continue
				printf %s "$initlist" |
					LC_ALL=C grep -qs "^$bp\$" ||
						printf '%s\n' "$bp"
			done
		)"
		buildlist="$(printf %s "$buildlist" |
			     LC_ALL=C grep -v '^$' ||:)"
		[ -n "$buildlist" ] &&
			verbose 'Generated build package file list.' ||
			fatal 'Failed to generate non-empty build package file list; check your sources.list'
	fi
else
	verbose 'Package file list for build is empty, bypassing.'
	buildlist=
fi # $build_list

if [ -z "$no_cache" ]; then
	printf %s "$initlist" >"$cache_dir/chroot/list.new/init"
	[ ! -s "$cache_dir/chroot/list.new/init" ] || echo >>"$cache_dir/chroot/list.new/init"
	printf %s "$buildlist" >"$cache_dir/chroot/list.new/build"
	[ ! -s "$cache_dir/chroot/list.new/build" ] || echo >>"$cache_dir/chroot/list.new/build"
fi

. hsh-sh-cache-chroot-functions
check_chroot_cache

if [ -n "$no_cache" -o -n "$rebuild_chroot_cache" ]; then
	create_chroot
else
	unpack_chroot_cache
fi

# Prepare /proc and /sys for potential mount.
if [ -n "$save_fakeroot" ]; then
	create_entry_fakeroot_header
	cat >>"$entry" <<__EOF__
rmdir $verbose /proc /sys >&2
__EOF__
	wlimit_time_elapsed=$wlimit_time_short wlimit_time_idle=$wlimit_time_short \
	    chrootuid1 </dev/null &&
		verbose "Removed old mount points." ||
		fatal "Failed to remove old mount points."
else
	rmdir $verbose chroot/proc chroot/sys >&2 &&
		verbose "Removed old mount points." ||
		fatal "Failed to remove old mount points."
fi
mkdir >&2 -m755 $verbose \
	chroot/proc \
	chroot/sys \
	chroot/sys/fs \
	chroot/sys/fs/cgroup &&
chgrp >&2 "$gid1" $verbose \
	chroot/proc \
	chroot/sys \
	chroot/sys/fs \
	chroot/sys/fs/cgroup &&
touch chroot/proc/filesystems &&
sed '/^$/q' /proc/cpuinfo > chroot/proc/cpuinfo &&
	verbose 'Prepared mount points.' ||
	fatal 'Failed to prepare mount points.'
if [ -n "$save_fakeroot" ]; then
	create_entry_fakeroot_header
	cat >>"$entry" <<__EOF__
chgrp $verbose proc /proc >&2
__EOF__
	wlimit_time_elapsed=$wlimit_time_short wlimit_time_idle=$wlimit_time_short \
	    chrootuid1 </dev/null &&
		verbose 'Updated faked /proc permissions.' ||
		fatal 'Failed to update faked /proc permissions.'
fi

. hsh-sh-cache-contents-functions
if [ -n "$no_contents_indices" ]; then
	contents_index_bin=
elif [ -n "$no_cache" ]; then
	create_contents
else
	update_contents
fi

if [ -n "$build_list" ]; then
	# Create RPM build directory tree.
	create_entry_header
	cat >>"$entry" <<__EOF__
[ ! -d /etc/skel ] || cp -a $verbose /etc/skel/.[^.]* "\$HOME"/ >&2 ||:
mkdir -p $verbose -- "\$TMPDIR" \
	"\$HOME/RPM"/{BUILD,SOURCES,SPECS,SRPMS,RPMS/noarch} >&2
cat >"\$HOME/.rpmmacros" <<EOF
%_tmppath \$TMPDIR
%_topdir \$HOME/RPM
EOF
cat >>"\$HOME/.rpmmacros" <<\EOF
%packager ${packager:-Automated package hasher <hasher@localhost>}
%buildhost ${USER:-`id -nu`}.hasher.altlinux.org
%__BTE hasher
%__nprocs $nprocs
${contents_index_bin:+%_pkg_contents_index_bin /.host/contents_index_bin}
%_rpmbuild_clean 0
${repackage_source:+%_rpmbuild_packagesource 1}
EOF
__EOF__

	wlimit_time_elapsed=$wlimit_time_short wlimit_time_idle=$wlimit_time_short wlimit_bytes_written=$wlimit_bytes_out \
	    chrootuid2 </dev/null &&
		verbose "Created RPM build directory tree." ||
		fatal 'Failed to create RPM build directory tree.'
fi # $build_list

purge_chroot_in
purge_chroot_out
